/**
@file
	PerfCounter.h
@brief

@author
	GengYong
@revision
	2005/11/05 * Created by GengYong.
*/


#ifndef SAF_HEADER_FILE_PERFCOUNTER_H
#define SAF_HEADER_FILE_PERFCOUNTER_H

#if (defined(_DEBUG) || !defined(DISABLE_PERFCOUNTER))
#	define ENABLE_PERFCOUNTER
#endif

#ifdef ENABLE_PERFCOUNTER
#	define REGISTER_PERFCOUNTER(name)	SAF::ProfileManager::Instance().SetProjectName(#name);
#	define PERFCOUNTER(name)												\
	static SAF::Private::_Counter_Holder_ _static_counter_(#name);			\
	SAF::Private::_Counter_Updater_ _counter_updater_(_static_counter_);

#include <ctime>
#include <string>
	//=============================================================
	// PerfCounter Begin here
	//=============================================================
	namespace SAF
	{
		namespace Private
		{
			class _Counter_Holder_;
			class _Counter_Updater_;

			inline unsigned long long PerfGetTick()
			{
#			if defined(WIN32)
				__asm rdtsc;
#			else
				unsigned long long cycle;
				__asm__ __volatile__ ("rdtsc" : "=A"(cycle)); 
				return cycle;
#			endif
			}
		}

		class ProfileManager
		{
			friend class Private::_Counter_Holder_;
			friend class Private::_Counter_Updater_;
		public:
			static ProfileManager& Instance()
			{
				static ProfileManager instance;
				return instance;
			}			
		public:
			void SetProjectName(const char * name)
			{
				if(name != NULL)
				{
					int i = 0;
					for (; (i < sizeof(projectName)/sizeof(projectName[0]) - 1) && (name[i] != '\0'); i++)
					{
						projectName[i] = name[i];
					}
					projectName[i] = '\0';
				}
				return;
			}
		private:
			class PerfProfile
			{
			public:
				PerfProfile *		next;
				unsigned long long	called;
				unsigned long long	usedtick;
				char				idenity[12];
			public:
				PerfProfile(const char * name):next(0), called(0), usedtick(0)
				{
					if (name == NULL)
					{
						idenity[0] = '\0';
						return;
					}

					int i = 0;
					for (; (i < sizeof(idenity)/sizeof(idenity[0]) - 1) && (name[i] != '\0'); i++)
					{
						idenity[i] = name[i];
					}
					idenity[i] = '\0';
					return;
				}
				~PerfProfile()
				{
					if (this->next != NULL)
					{
						delete next;
						next = NULL;
					}
					return;
				}
			};
		
		private:
			PerfProfile * AllocProfile(const char * iden)
			{
				if (profileTail != NULL)
				{
					profileTail->next = new PerfProfile(iden);
					profileTail = profileTail->next;
				}
				else
				{
					profileHead = profileTail = new PerfProfile(iden);
				}
				return profileTail;
			}
		
			void FreeProfile(PerfProfile *)
			{
				return;
			}
		
			void EnterProfile(PerfProfile * profile)
			{
				callDepth++;
			}
			void LeaveProfile(PerfProfile * profile)
			{
				callDepth--;
			}
		private:
			ProfileManager():profileHead(0), profileTail(0)
			{
				projectName[0] = '\0';
				startTime = time(NULL);
				startTick = Private::PerfGetTick();
				return;
			}

			~ProfileManager()
			{
				unsigned long long totalTicks = Private::PerfGetTick() - startTick;
				time_t duration = time(NULL) - startTime;
				printf("==============================================================================\n");
				printf("Running time: %llu million ticks in %llu seconds - [%s]\n",
					(unsigned long long)(totalTicks/1000000), (unsigned long long)(duration), (projectName == NULL? "unknown":projectName));
				printf("------------------------------------------------------------------------------\n");
				printf("           ticks     called   ratio(%%)        average   name\n");
				printf("------------------------------------------------------------------------------\n");
				for (ProfileManager::PerfProfile * point = profileHead; point != NULL; point = point->next)
				{
					printf("%16llu %10llu   %8.4lf %14llu   %s\n",
						point->usedtick, point->called,
						(totalTicks == 0 ? (double)(0.0f) : (double)point->usedtick/(double)(totalTicks) * 100.0f),
						(point->called == 0 ? 0:point->usedtick/point->called),
						point->idenity);
				}
				printf("==============================================================================\n");

				if (profileHead != NULL)
				{
					delete profileHead;
					profileHead = profileTail = NULL;
				}
				return;
			}
		private:
			PerfProfile *		profileHead;
			PerfProfile *		profileTail;
			unsigned long long	startTick;
			time_t				startTime;
			char 				projectName[8];
			int					callDepth;
		};

		namespace Private
		{
			class _Counter_Holder_ {
				friend class _Counter_Updater_;
			public:
				_Counter_Holder_(const char * iden) {
					profile = SAF::ProfileManager::Instance().AllocProfile(iden);
				}
				~_Counter_Holder_() {
					SAF::ProfileManager::Instance().FreeProfile(profile);
				}
			private:
				SAF::ProfileManager::PerfProfile * profile;
			};

			class _Counter_Updater_ {
			public:
				_Counter_Updater_(_Counter_Holder_ & c):counterHolder(c){
					SAF::ProfileManager::Instance().EnterProfile(counterHolder.profile);
					counterHolder.profile->called++;
					startTick = SAF::Private::PerfGetTick();
				}
				~_Counter_Updater_(){
					counterHolder.profile->usedtick += SAF::Private::PerfGetTick()- startTick;	
					SAF::ProfileManager::Instance().LeaveProfile(counterHolder.profile);
				}
			private:
				void operator=(const _Counter_Updater_&);
				_Counter_Holder_ &	counterHolder;
				unsigned long long	startTick;
			};
		}
	}

#else
#	define REGISTER_PERFCOUNTER(name)
#	define PERFCOUNTER(name)
#endif

#endif	//#ifndef SAF_HEADER_FILE_PERFCOUNTER_H
